
#import "YDRuntimeController.h"
#import "YDMethodSwizzling.h"

#import "YDRuntimePeople.h"
#import "YDRuntimePeople+YDAddProperty.h"

//Runtime需要的头文件
#import <objc/runtime.h>
#import <objc/message.h>

#import "YDArchive.h"
#import "YDUserData.h"
#import "YDDateStorage.h"

#import "YDCat.h"

#import "YDPerson.h"
#import "YDPerson+YDExtension.h"

@interface YDRuntimeController ()

/** 方法交互 */
@property (nonatomic, strong) UIButton *functionExchangeButton;
/** 添加属性 */
@property (nonatomic, strong) UIButton *addNewPropertiesButton;
/** 获取类详情 */
@property (nonatomic, strong) UIButton *getClassDetailsButton;
/** 直接调用方法 */
@property (nonatomic, strong) UIButton *callFunctionButton;

@end

@implementation YDRuntimeController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = @"Runtime";
    
//    YDMethodSwizzling *method = [[YDMethodSwizzling alloc] init];
//
//    [method printA];
//
//    [YDMethodSwizzling printC];
//
//    YDRuntimePeople *people = [[YDRuntimePeople alloc] init];
//    people.peopleName = @"赵钱孙";
//    people.peopleAge = @"589";
//    people.peopleSex = @"男";
//
//    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: people ->: %@ ->:", people);
//    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: people ->: name ->: %@ ->:", people.peopleName);
//    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: people ->: age ->: %@ ->:", people.peopleAge);
//    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: people ->: sex ->: %@ ->:", people.peopleSex);
//
//    //获取类的详细信息
//    [self getPeoplePropertyList];
//    [self getPeopleIvarList];
//    [self getPeopleMethodList];
//    [self getPeopleProtocolList];
//
//    //高频率调用同一个方法
//    [self highFrequencyMethod];
//
//    //动态修改属性
//    [self modifyPeopleProperty];
    
//    [self writeArchiveList];
//
//    [self readArchiveList];
    
    
//    YDCat *cat = [[YDCat alloc] init];
//    [cat printTiger];
//    [cat printLeopard];
    
    
//    YDPerson *person = [[YDPerson alloc] init];
//    person.personName = @"赵钱孙";
//    [person personEat];
//    [person performSelector:@selector(personEat)];
//
//
//    ((id (*) (id, SEL)) (void *)objc_msgSend) (person, @selector(personEat));
//    ((id (*) (id, SEL)) (void *)objc_msgSend) ([YDPerson class], @selector(personEat));
//
//    ((void (*) (id, SEL)) (void *)objc_msgSend) (person, @selector(personEat));
//
//    ((void (*) (id, SEL)) objc_msgSend) (person, @selector(personEat));
//
//    NSLog(@"wuyezhiguhun -->: person -->: name: %@", person.personName);

    
    /**
     * 方法交换
     */
    //[self functionExchange];
    
    
    /**
     * 添加新属性
     */
    //[self yd_AddNewProperties];
    
    /**
     * 获取类详情
     */
    //[self yd_GetClassDetails];
    
    /**
     * 直接调用函数
     */
    //[self yd_CallClassFunction];
    
    [self addAllViews];
}

#pragma mark ==== 添加所有的View ====
- (void)addAllViews {
    [self.view addSubview:self.functionExchangeButton];
    [self.view addSubview:self.addNewPropertiesButton];
    [self.view addSubview:self.getClassDetailsButton];
    [self.view addSubview:self.callFunctionButton];
}

#pragma mark ==== 方法交换 ====
/**
 * 方法交换
 */
- (void)functionExchange {
    YDMethodSwizzling *method = [[YDMethodSwizzling alloc] init];

    [method printA];
}

#pragma mark ==== 添加新属性 ====
/**
 * 添加新属性
 */
- (void)yd_AddNewProperties {
    YDRuntimePeople *people = [[YDRuntimePeople alloc] init];
    people.peopleName = @"赵钱孙";
    people.peopleAge = @"589";
    people.peopleSex = @"男";
    
    
    NSLog(@"wuyezhiguhun - :<= YDRuntimeController =>: peopleSex =>: %@", people.peopleSex);
}

#pragma mark ==== 获取类详情 ====
/**
 * 获取类详情
 */
- (void)yd_GetClassDetails {
    //获取类的详细信息
    //获取属性列表
    [self getPeoplePropertyList];
    [self getPeopleIvarList];
    [self getPeopleMethodList];
    [self getPeopleProtocolList];
}

#pragma mark ==== 直接调用函数 ====
/**
 * 直接调用函数
 */
- (void)yd_CallFunction {
    
    [self yd_CallClassFunction];
    
    NSLog(@"\n========================================\n");
    
    [self yd_CallObjectFunction];
    
}
/**
 * Runtime 直接调用类函数
 */
- (void)yd_CallClassFunction {
    ((id (*) (id, SEL)) (void *)objc_msgSend) ([YDPerson class], @selector(personEat));
}
/**
 * Runtime 直接调用对象函数
 */
- (void)yd_CallObjectFunction {
    YDPerson *person = [[YDPerson alloc] init];
    ((id (*) (id, SEL)) (void *)objc_msgSend) (person, @selector(personEat));
    
    //((id (*) (id, SEL)) (void *)objc_msgSend) ([YDPerson class], @selector(personEat));
}


#pragma mark ==== 归档接档 ====

- (void)testArchive {
    YDUserData *userData = [[YDUserData alloc] init];
    userData.name = @"赵钱孙";
    userData.icon = @"美美哒";
    userData.height = @"180cm";
    userData.age = 20;
    userData.money = @152;
    userData.address = @"谢香丽舍大道";
    
    //把更新后的数据进行归档
    NSString *fileName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"userDataFile"];
    [NSKeyedArchiver archiveRootObject:@[userData] toFile:fileName];
    //    [NSKeyedArchiver archivedDataWithRootObject:@[userData] requiringSecureCoding:YES error:nil];
    
    
    NSArray *userArray = [NSKeyedUnarchiver unarchiveObjectWithFile:fileName];
    
    
    YDUserData *user = [userArray firstObject];
    
    NSLog(@"wuyezhiguhun ->: user ->: %@", user);
    NSLog(@"wuyezhiguhun ->: user ->: name ->: %@", user.name);
    NSLog(@"wuyezhiguhun ->: user ->: icon ->: %@", user.icon);
    NSLog(@"wuyezhiguhun ->: user ->: money ->: %@", user.money);
    NSLog(@"wuyezhiguhun ->: user ->: age ->: %ld", user.age);
}

/** 写入 */
- (void)writeArchiveList {
    YDArchive *archive = [[YDArchive alloc] init];
    archive.archiveId = @"11111111";
    /**** NSString和NSMutableString XML归解档 ****/
    NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"] stringByAppendingPathComponent:@"archive.txt"];
    NSString *docuPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *archivePath = [NSString stringWithFormat:@"%@/%@" ,docuPath, @"home"];
    // atomically：这个参数意思是如果为YES，则保证文件的写入原子性。就是说会先创建一个临时文件,直到文件内容写入成功再导入到目标文件里.如果为NO,则直接写入目标文件里.
    
    //把更新后的数据进行归档
    NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"dataFile"];
    
    NSLog(@":<- wuyezhiguhun ->: %@ ->:",filename);
    
    NSArray *array = @[archive];
    
    if ([YDDateStorage archiveRootObject:array toFileName:@"archive"]) {
        NSLog(@":<- wuyezhiguhun -> success ->: ->:");
    }
    
    if ([NSKeyedArchiver archiveRootObject:array toFile:filename]) {
        
    }

}
/** read 读 */
- (void)readArchiveList {
    //把更新后的数据进行归档
    NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"dataFile"];
    NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
    NSLog(@":<- wuyezhiguhun ->: %@ ->:",arr);
    
    NSArray *array = [YDDateStorage unarchiveObjectWithFileName:@"archive"];
    NSLog(@":<- wuyezhiguhun -> array ->: %@ ->:",array);
    
    YDArchive *archive = [array firstObject];
    
    NSLog(@":<- wuyezhiguhun -> archive ->: archiveId ->: %@ ->:",archive.archiveId);
}
#pragma mark ==== 动态操作属性 ====
//动态修改属性数据
- (void)modifyPeopleProperty {
    YDRuntimePeople *people = [[YDRuntimePeople alloc] init];
    people.peopleName = @"赵钱孙";
    people.peopleAge = @"589";
    people.peopleSex = @"男";
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: modifyPeopleProperty ->: people ->: peopleName: %@",[people valueForKey:@"peopleName"]); //null
    //第一步：遍历对象的所有属性
    unsigned int count;
    Ivar *ivarList = class_copyIvarList([people class], &count);
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: modifyPeopleProperty ->: people ->: count: %d", count);
    
    for (int i = 0; i < count; i++) {
        //第二步：获取每个属性名
        Ivar ivar = ivarList[i];
        const char *ivarName = ivar_getName(ivar);
        NSString *propertyName = [NSString stringWithUTF8String:ivarName];
        
        NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: modifyPeopleProperty ->: people ->: propertyName: %@", propertyName);
        
        if ([propertyName isEqualToString:@"_peopleName"]) {
            //第三步：匹配到对应的属性，然后修改；注意属性带有下划线
            object_setIvar(people, ivar, @"梧雨北辰");
        }
    }
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: modifyPeopleProperty ->: people ->: peopleName: %@",[people valueForKey:@"peopleName"]); //梧雨北辰
}

#pragma mark ==== 方法动态解析与消息转发 ====
/** 动态方法解析：动态添加方法 */
/** 解决无法响应崩溃问题 */


#pragma mark ==== 解决同一方法高频率调用的频率问题 ====
/** 高频率调用同一个方法 */
- (void)highFrequencyMethod {
    
    YDRuntimePeople *people = [[YDRuntimePeople alloc] init];
    
    void (*setter)(id, SEL, BOOL);

    setter = (void (*)(id, SEL, BOOL))[YDRuntimePeople methodForSelector:@selector(printRuntimePeople)];
    for (int i = 0 ; i < 1000 ; i++ ) {
        setter(people, @selector(printRuntimePeople), YES);
        
        setter(self, @selector(printRuntimePeople), YES);
    }
}

#pragma mark ==== 获取类的详细信息 ====
/** 获取类属性列表 */
- (void)getPeoplePropertyList {
    unsigned int count;
    objc_property_t *propertyList = class_copyPropertyList(NSClassFromString(@"YDRuntimePeople"), &count);
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeoplePropertyList ->: people ->: count ->: %d ->:", count);
    
    for (unsigned int i = 0; i < count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeoplePropertyList ->: people ->: PropertyName(%d): %@ ->:", i,[NSString stringWithUTF8String:propertyName]);
    }
    free(propertyList);
}
/** 获取所有成员变量 */
- (void)getPeopleIvarList {
    unsigned int count;
    Ivar *ivarList = class_copyIvarList(NSClassFromString(@"YDRuntimePeople"), &count);
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleIvarList ->: people ->: count ->: %d ->:", count);
    
    for (int i= 0; i<count; i++) {
        Ivar ivar = ivarList[i];
        const char *ivarName = ivar_getName(ivar);
        NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleIvarList ->: people ->: Ivar(%d): %@ ->: ", i, [NSString stringWithUTF8String:ivarName]);
    }
    free(ivarList);
}
/** 获取所有方法 */
- (void)getPeopleMethodList {
    unsigned int count;
    Method *methodList = class_copyMethodList(NSClassFromString(@"YDRuntimePeople"), &count);
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleMethodList ->: people ->: count ->: %d ->:", count);
    
    for (unsigned int i = 0; i < count; i++) {
        Method method = methodList[i];
        SEL mthodName = method_getName(method);
        NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleIvarList ->: people ->: MethodName(%d): %@",i,NSStringFromSelector(mthodName));
    }
    free(methodList);
}
/** 获取当前遵循的所有协议 */
- (void)getPeopleProtocolList {
    unsigned int count;
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList(NSClassFromString(@"YDRuntimePeople"), &count);
    
    NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleProtocolList ->: people ->: count ->: %d ->:", count);
    
    for (int i = 0; i < count; i++) {
        Protocol *protocal = protocolList[i];
        const char *protocolName = protocol_getName(protocal);
        NSLog(@":<- wuyezhiguhun ->: YDRuntimeController ->: PeopleIvarList ->: people ->: protocol(%d): %@",i, [NSString stringWithUTF8String:protocolName]);
    }
    free(protocolList);
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    YDMethodSwizzling *method = [[YDMethodSwizzling alloc] init];
    
    [method printB];
    
    [YDMethodSwizzling printD];
    
}

- (UIButton *)newButtonWithFrame:(CGRect)frame title:(NSString *)title action:(SEL)action {
    
    UIButton *button = [[UIButton alloc] initWithFrame:frame];
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    button.backgroundColor = [UIColor whiteColor];
    [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
    button.titleLabel.font = [UIFont systemFontOfSize:15];
    
    button.layer.cornerRadius = 5;
    button.layer.masksToBounds = YES;
    
    return button;
}

- (UIButton *)functionExchangeButton {
    if (!_functionExchangeButton) {
        _functionExchangeButton = [self newButtonWithFrame:CGRectMake(50, 120, 100, 40) title:@"方法交换" action:@selector(functionExchange)];
    }
    return _functionExchangeButton;
}
- (UIButton *)addNewPropertiesButton {
    if (!_addNewPropertiesButton) {
        _addNewPropertiesButton = [self newButtonWithFrame:CGRectMake(200, 120, 100, 40) title:@"添加属性" action:@selector(yd_AddNewProperties)];
    }
    return _addNewPropertiesButton;
}
/** 获取类详情 */
- (UIButton *)getClassDetailsButton {
    if (!_getClassDetailsButton) {
        _getClassDetailsButton = [self newButtonWithFrame:CGRectMake(50, 200, 100, 40) title:@"获取类详情 " action:@selector(yd_GetClassDetails)];
    }
    return _getClassDetailsButton;
}
- (UIButton *)callFunctionButton {
    if (!_callFunctionButton) {
        _callFunctionButton = [self newButtonWithFrame:CGRectMake(200, 200, 100, 40) title:@"直接调用方法" action:@selector(yd_CallFunction)];
    }
    return _callFunctionButton;
}


@end
